/**
  ******************************************************************************
  * @file    display.c
  * @author  N-K-J
  * @version V0.0.1
  * @date    2012/12/11
  * @brief   display.c
  ******************************************************************************
 **/

#include "display.h"


#if DISPLAYCB 

typedef struct _displaycb
{
    uint32_t id;                        //显示器ID
    void (*init)(void);                 //初始化
    void (*setXYZ)(int x,int y,int z);  //设置显示坐标
    void (*setConversely)(uint8_t con); //设置是否反显
    void (*setFrontRGB)(uint32_t rgb);  //24位前景色设置
    void (*setBackRGB)(uint32_t rgb);   //24位背景色设置
    void (*clear)(void);                //清屏
    void (*putChar)(int c);             //打印一个字符
#if GRAPHICDISPLAYEN
    void (*setPoint)(int x,int y,uint32_t rgb); //显示一个点
    void (*refresh)(void);                      //屏幕刷新
#endif
}DisplayCB,*pDisplayCB;

static volatile DisplayCB displaycb[MAXMONITORNUM];
static volatile uint32_t currentDisplayID = 0;  //当前选中显示器ID,0为没有选择显示器或无显示器

#if GRAPHICDISPLAYEN
static uint16_t displayX = 0,displayY = 0,displayZ = 0;
static uint16_t displayAsciiFontLib = FONT_ASCII_8X8;
static uint16_t displayHanziFontLib = FONT_HANZI_16X16;
static uint16_t displayFontSize = 1;
static uint32_t displayFrontRGB = 0xff;
static uint32_t displayBackRGB = 0;
static uint8_t displayConversely = 0;
#endif

static int msc_print(char **out, const char *format, va_list args);


/* 显示控制块初始化
 * 函数名：void displayInit(void)
 * 输入：
 * 返回：
*/
void displayInit(void)
{
    uint32_t i = 0;
    for(i = 0;i < (sizeof(DisplayCB) * MAXMONITORNUM);i ++)
    {
        ((uint8_t *)displaycb)[i] = 0;
    }
    currentDisplayID = 0;
}

/* 注册一个显示器
 * 函数名：eDerrorcode displayRegister(uint32_t *id,void *init,void *setXYZ,void *setConversely,void *setFrontRGB,
 *                                     void *setBackRGB,void *clear,void *putChar,void *setPoint,void *refresh)
 * 输入：
 *      id               显示器ID,返回
 *      init             显示器初始化函数
 *      setXYZ           显示设置坐标函数
 *      setConversely    反显设置函数
 *      setFrontRGB      显示前景色函数
 *      setBackRGB       显示背景色函数
 *      clear            清屏函数
 *      putChar          打印一个字符函数
 *      setPoint         显示一个点
 *      refresh          屏幕刷新
 * 返回：见队显示错误码
 */
eDerrorcode displayRegister(uint32_t *id,void *init,void *setXYZ,void *setConversely,void *setFrontRGB,
                            void *setBackRGB,void *clear,void *putChar,void *setPoint,void *refresh)
{
    uint32_t i = 0;
    if((id == NULL) || (putChar == NULL))
        return D_FAIL;
#if GRAPHICDISPLAYEN
    if((setPoint == NULL) || (refresh == NULL))
        return D_FAIL;
#endif
    for(i = 0;i < MAXMONITORNUM;i ++)
    {
        if(displaycb[i].id == 0)
        {
            *id = displaycb[i].id = (i + 1);
            displaycb[i].init = init;
            displaycb[i].setXYZ = setXYZ;
            displaycb[i].setConversely = setConversely;
            displaycb[i].setFrontRGB = setFrontRGB;
            displaycb[i].setBackRGB = setBackRGB;
            displaycb[i].clear = clear;
            displaycb[i].putChar = putChar;
            currentDisplayID = displaycb[i].id;
#if GRAPHICDISPLAYEN
            displaycb[i].setPoint = setPoint;
            displaycb[i].refresh = refresh;
#endif
            return D_SUCCESS;
        }
    }
    return D_FAIL;
}

/* 删除一个显示器
 * 函数名：eDerrorcode displayDelete(uint32_t *id)
 * 输入：
 *     id  显示器ID
 * 返回：见队显示错误码
 */
eDerrorcode displayDelete(uint32_t *id)
{
    uint32_t i = 0;
    for(i = 0;i < MAXMONITORNUM;i ++)
    {
        if(displaycb[i].id == *id)
        {
            if(currentDisplayID == *id)
                currentDisplayID = 0;
            *id = displaycb[i].id = 0;
            displaycb[i].init = 0;
            displaycb[i].setXYZ = 0;
            displaycb[i].setConversely = 0;
            displaycb[i].setFrontRGB = 0;
            displaycb[i].setBackRGB = 0;
            displaycb[i].clear = 0;
            displaycb[i].putChar = 0;
#if GRAPHICDISPLAYEN
            displaycb[i].setPoint = 0;
            displaycb[i].refresh = 0;
#endif
            return D_SUCCESS;
        }
    }
    return D_FAIL;
}

/* 选择一个显示器并初始化
 * 函数名：eDerrorcode displaySelect(uint32_t id)
 * 输入：
 *     id  显示器ID
 * 返回：见队显示错误码
 */
eDerrorcode displaySelect(uint32_t id)
{
    uint32_t i = 0;
    for(i = 0;i < MAXMONITORNUM;i ++)
    {
        if(displaycb[i].id == id)
        {
            currentDisplayID = displaycb[i].id;
            if(displaycb[i].init)
                displaycb[i].init();

            return D_SUCCESS;
        }
    }
    return D_FAIL;
}

/* 清除屏幕
 * 函数名：void displayClear(void)
 * 输入：
 * 返回：
 */
void displayClear(void)
{
    if(!currentDisplayID)
        return;
    if(displaycb[currentDisplayID - 1].clear)
        displaycb[currentDisplayID - 1].clear();
}

/* 类似PC printf(),可控制颜色，坐标等通过\033
 * 函数名：void display(const char *format, ...)
 * 输入：
 *      format 格式控制
 *      ...    输入参数
 * 返回：
 */
void display(const char *format, ...)
{
    va_list ap;
    va_start(ap,format);

    if(!currentDisplayID)
        return;

    msc_print(0,format,ap);
}

/* 通过定位坐标来显示
 * 函数名：void displayByXYZ(int x,int y,int z,const char *format, ...)
 * 输入：
 *      x,y,z  坐标
 *      format 格式控制
 *      ...    输入参数
 * 返回：
 */
void displayByXYZ(int x,int y,int z,const char *format, ...)
{
    va_list ap;
    va_start(ap,format);

    if(!currentDisplayID)
        return;
    if(displaycb[currentDisplayID - 1].setXYZ)
        displaycb[currentDisplayID - 1].setXYZ(x,y,z);

    msc_print(0,format,ap);
}

/* 通过定位坐标来反显示
 * 函数名：void displayByXYZConversely(int x,int y,int z,const char *format, ...)
 * 输入：
 *      x,y,z  坐标
 *      format 格式控制
 *      ...    输入参数
 * 返回：
 */
void displayByXYZConversely(int x,int y,int z,const char *format, ...)
{
    va_list ap;
    va_start(ap,format);

    if(!currentDisplayID)
        return;
    if(displaycb[currentDisplayID - 1].setXYZ)
        displaycb[currentDisplayID - 1].setXYZ(x,y,z);
    if(displaycb[currentDisplayID - 1].setConversely)
        displaycb[currentDisplayID - 1].setConversely(1);

    msc_print(0,format,ap);

    if(displaycb[currentDisplayID - 1].setConversely)
        displaycb[currentDisplayID - 1].setConversely(0);
}

#if GRAPHICDISPLAYEN
/* 屏幕刷新
 * 函数名：void displayRefresh(void)
 * 输入：
 * 返回：
 */
void displayRefresh(void)
{
    if(!currentDisplayID)
        return;
    if(displaycb[currentDisplayID - 1].refresh)
        displaycb[currentDisplayID - 1].refresh();
}

/* 设置坐标
 * 函数名：void displaySetXYZ(void)
 * 输入：
 *      x x
 *      y y
 *      z z
 * 返回：
 */
void displaySetXYZ(uint16_t x,uint16_t y,uint16_t z)
{
    if(!currentDisplayID)
        return;
    displayX = x;
    displayY = y;
    displayZ = z;
}

/* 设置颜色
 * 函数名：void displaySetXYZ(void)
 * 输入：
 *      f 前景色
 *      b 背景色
 * 返回：
 */
void displaySetColor(uint32_t f,uint32_t b)
{
    if(!currentDisplayID)
        return;
    displayFrontRGB = f;
    displayBackRGB = b;
}

/* 设置反显
 * 函数名：void displaySetConversely(uint8_t c)
 * 输入：
 *      c 0,正显，1,反显
 * 返回：
 */
void displaySetConversely(uint8_t c)
{
    if(!currentDisplayID)
        return;
    displayConversely = c;
}

/* 设置字号,暂为当前子库放大倍数
 * 函数名：void displaySetFontSize(uint8_t s)
 * 输入：
 *      s size
 *      0 为采用当前设置
 * 返回：
 */
void displaySetFontSize(uint16_t s)
{
    if(!currentDisplayID)
        return;
    if(!s)  return;
    displayFontSize = s;
}

/* 选择字库
 * 函数名：void displaySelectFontLib(uint8_t asciilib,uint8_t hanzilib)
 * 输入：
 *      asciilib    ascii码库
 *      hanzilib    汉字库
 *      0 为采用当前设置
 * 返回：
 */
void displaySelectFontLib(uint8_t asciilib,uint8_t hanzilib)
{
    if(!currentDisplayID)
        return;
    if(asciilib)
        displayAsciiFontLib = asciilib;
    if(hanzilib)
        displayHanziFontLib = hanzilib;
}

/* 输出一个字符
 * 函数名：void displayPutChar(int ch)
 * 输入：
 *      ch 字符
 * 返回：
 */
void displayPutChar(int ch)
{
    if(!currentDisplayID)
        return;
    static uint8_t i = 0;
    i ++;
    if(ch & 0x80)
    {
        static char buf[2] = {0};
        buf[i - 1] = (char)ch;
        if(i == 2)
        {
            i = 0;
            switch(displayHanziFontLib)
            {
                case FONT_HANZI_16X16:
                    displayGB2312_16x16(displayX,displayY,displayFontSize,buf,displayFrontRGB);
                    displayX += 16;
                break;

                default: break;
            }
        }
    }
    else
    {
        i = 0;
        switch(displayAsciiFontLib)
        {
            case FONT_ASCII_3X5:
                displayASCII_3x5(displayX,displayY,displayFontSize,ch,displayFrontRGB);
                displayX += 3;
            break;

            case FONT_ASCII_5X7:
                displayASCII_5x7(displayX,displayY,displayFontSize,ch,displayFrontRGB);
                displayX += 5;
            break;

            case FONT_ASCII_8X8:
                displayASCII_8x8(displayX,displayY,displayFontSize,ch,displayFrontRGB);
                displayX += 8;
            break;

            case FONT_ASCII_8X16:
                displayASCII_8x16(displayX,displayY,displayFontSize,ch,displayFrontRGB);
                displayX += 8;
            break;

            default: break;
        }
    }
}

/* Bresenham 画线
 * 函数名：void displayLine(int16_t sx,int16_t sy,int16_t ex,int16_t ey,int16_t lw,uint32_t rgb)
 * 输入：
 *      sx,sy   起始坐标
 *      ex,ey   终止坐标
 *      lw      线宽
 *      rgb     颜色
 * 返回：
 */
void displayLine(int16_t sx,int16_t sy,int16_t ex,int16_t ey,uint16_t lw,uint32_t rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;

    int dx = ex - sx;
    int dy = ey - sy;
    int ux = ((dx > 0) << 1) - 1;
    int uy = ((dy > 0) << 1) - 1;
    int x = sx, y = sy, eps = 0,w = lw;

    dx = (dx > 0) ? (dx) : (-dx);
    dy = (dy > 0) ? (dy) : (-dy);

    if(dx > dy) 
    {
        for (x = sx; x != ex + ux; x += ux)
        {
            displaycb[currentDisplayID - 1].setPoint(x,y,rgb);
            while(w && (-- w))
                displaycb[currentDisplayID - 1].setPoint(x,y + w,rgb);
            w = lw;

            eps += dy;
            if((eps << 1) >= dx)
            {
                y += uy; 
                eps -= dx;
            }
        }
    }
    else
    {
        for(y = sy; y != ey + uy; y += uy)
        {
            displaycb[currentDisplayID - 1].setPoint(x,y,rgb);
            while(w && (-- w))
                displaycb[currentDisplayID - 1].setPoint(x + w,y,rgb);
            w = lw;

            eps += dx;
            if ((eps << 1) >= dy)
            {
                x += ux; 
                eps -= dy;
            }
        }
    }             
}

/* 画矩形
 * 函数名：void displayRectangle(uint16_t x,uint16_t y,uint16_t len,uint16_t wid,uint16_t linew,uint32_t rgb)
 * 输入：
 *      x,y         起始坐标
 *      len,wid     长，宽
 *      linew       线宽
 *      rgb         颜色
 * 返回：
 */
void displayRectangle(uint16_t x,uint16_t y,uint16_t len,uint16_t wid,uint16_t linew,uint32_t rgb)
{
    uint16_t dw = 0;
    displayLine(x,y,x + len - 1,y,((linew < wid) ? linew : wid),rgb);
    displayLine(x,y,x,y + wid - 1,((linew < len) ? linew : len),rgb);
    dw = ((linew < len) ? linew : len);
    dw = ((dw > 1) ? (dw - 1) : 0);
    displayLine(x + len - 1 - dw,y,x + len - 1 - dw,y + wid - 1,((linew < len) ? linew : len),rgb);
    dw = ((linew < wid) ? linew : wid);
    dw = ((dw > 1) ? (dw - 1) : 0);
    displayLine(x,y + wid - 1 - dw,x + len - 1,y + wid - 1 - dw,((linew < wid) ? linew : wid),rgb);
}

/* 画三角形,TODO 填充没实现
 * 函数名：void displayTriangle(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,uint16_t x3,uint16_t y3,uint16_t linew,uint32_t rgb)
 * 输入：
 *      x,y         3顶点坐标,必须这样排列,左边的顶点,中间的顶点,右边的点
 *      linew       线宽
 *      rgb         颜色
 * 返回：
 */
void displayTriangle(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,uint16_t x3,uint16_t y3,uint16_t linew,uint32_t rgb)
{
    displayLine(x1,y1,x2,y2,1,rgb);
    displayLine(x2,y2,x3,y3,1,rgb);
    displayLine(x3,y3,x1,y1,1,rgb);
}

static void ellipsePlot(int xc,int yc,int x,int y,uint16_t lw,uint32_t rgb)
{
    int w = ((lw > 0) ? lw : 1);

    while(w --)
    {
        displaycb[currentDisplayID - 1].setPoint(xc + x - w,yc + y - w,rgb);
        displaycb[currentDisplayID - 1].setPoint(xc + x - w,yc - y + w,rgb);
        displaycb[currentDisplayID - 1].setPoint(xc - x + w,yc + y - w,rgb);
        displaycb[currentDisplayID - 1].setPoint(xc - x + w,yc - y + w,rgb);
    }
}

/* Bresenham 画椭圆
 * 函数名：void displayEllipse(int16_t xc,int16_t yc,uint16_t a,uint16_t b,uint16_t lw,uint32_t rgb)
 * 输入：
 *      xc,yc   中心坐标
 *      a,b     半长短轴
 *      lw      线宽
 *      rgb     颜色
 * 返回：
 */
void displayEllipse(int16_t xc,int16_t yc,uint16_t a,uint16_t b,uint16_t lw,uint32_t rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;
    
    int sqa = a * a;
    int sqb = b * b;
    int x = 0;
    int y = b;
    int d = 2 * sqb - 2 * b * sqa + sqa;

    ellipsePlot(xc,yc,x,y,lw,rgb);
    int P_x = (int)(0.5 + (double)sqa/sqrt((double)(sqa+sqb)));
    while(x <= P_x)
    {
        if(d < 0)
        {
            d += 2 * sqb * (2 * x + 3);
        }
        else
        {
            d += 2 * sqb * (2 * x + 3) - 4 * sqa * (y - 1);
            y--;
        }
        x++;
        ellipsePlot(xc,yc,x,y,lw,rgb);
    }

    d = sqb * (x * x + x) + sqa * (y * y - y) - sqa * sqb;
    while(y >= 0)
    {
        ellipsePlot(xc,yc,x,y,lw,rgb);
        y--;
        if(d < 0)
        {
            x++;
            d = d - 2 * sqa * y - sqa + 2 * sqb * x + 2 * sqb;
        }
        else
        {
            d = d - 2 * sqa * y - sqa;
        }
    }
}

/* Bresenham 画圆
 * 函数名：void displayCircular(int16_t x,int16_t y,uint16_t r,uint16_t lw,uint32_t rgb)
 * 输入：
 *      x,y     中心坐标
 *      r       半径
 *      lw      线宽
 *      rgb     颜色
 * 返回：
 */
void displayCircular(int16_t x,int16_t y,uint16_t r,uint16_t lw,uint32_t rgb)
{
    displayEllipse(x,y,r,r,lw,rgb);
}

/* 画图,按行扫描
 * 函数名：void displayPicture(int16_t x,int16_t y,uint16_t r,uint16_t lw,uint32_t rgb)
 * 输入：
 *      x,y     起始坐标
 *      w,h     宽,高
 *      depth   颜色深度 1,8,16,24
 *      rgb     颜色指针
 * 返回：
 */
void displayPicture(int16_t x,int16_t y,uint16_t w,uint16_t h,uint8_t depth,void *rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;

    int i = 0;
    switch(depth)
    {
        case 8:
            for(i = 0;i < w * h;i ++)
            {
                displaycb[currentDisplayID - 1].setPoint(x + i % w,y + i / w,((uint8_t *)rgb)[i]);
            }
        break;
        case 16:
            for(i = 0;i < w * h;i ++)
            {
                displaycb[currentDisplayID - 1].setPoint(x + i % w,y + i / w,((uint16_t *)rgb)[i]);
            }
        break;
        case 24:
        case 32:
            for(i = 0;i < w * h;i ++)
            {
                displaycb[currentDisplayID - 1].setPoint(x + i % w,y + i / w,((uint32_t *)rgb)[i]);
            }
        break;
        default:
            for(i = 0;i < w * h;i ++)
            {
                displaycb[currentDisplayID - 1].setPoint(x + i % w,y + i / w,(((uint8_t *)rgb)[i / 8] & (0x01 << (i % 8))));
            }
        break;
    }
}

/* 画GB2312 16*16 汉字
 * 函数名：void displayGB2312_16x16(int16_t x,int16_t y,uint8_t n,char *hz,uint32_t rgb)
 * 输入：
 *      x,y     坐标
 *      n       放大倍数
 *      hz      汉字
 *      rgb     颜色
 * 返回：
 */
void displayGB2312_16x16(int16_t x,int16_t y,uint8_t n,char *hz,uint32_t rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;
    uint16_t i = 0,j = 0;
    uint16_t tmp = 0;
#if USE_ZDY_HZ_LIB_EN   
    uint8_t *phz = (uint8_t *)getHzMData(hz,2);
#else
    uint8_t *phz = (uint8_t *)getHzMData(hz,1);
#endif
    if(phz == NULL)
        return;

    for(i = 0;i < 16;i ++)
    {
        tmp = (phz[i * 2] << 8) | phz[i * 2 + 1];
        if(displayConversely)
            tmp = ~tmp;
        for(j = 0;j < 16;j ++)
        {
            if(tmp & (0x8000 >> j))
                displaycb[currentDisplayID - 1].setPoint(j + x,i + y,rgb);
            else
                displaycb[currentDisplayID - 1].setPoint(j + x,i + y,displayBackRGB);
        }
    }
}

/* 画ascii 3*5 字符
 * 函数名：void displayASCII_3x5(int16_t x,int16_t y,uint8_t n,char ch,uint32_t rgb)
 * 输入：
 *      x,y     坐标
 *      n       放大倍数
 *      ch      字符
 *      rgb     颜色
 * 返回：
 */
void displayASCII_3x5(int16_t x,int16_t y,uint8_t n,char ch,uint32_t rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;
    uint16_t i = 0,j = 0;
    uint8_t tmp = 0;
    for(i = 0;i < 3;i ++)
    {
        tmp = ascii3x5Lib[(ch - 0x20) * 3 + i];
        if(displayConversely)
            tmp = ~tmp;
        for(j = 0;j < 5;j ++)
        {
            if(tmp & (0x01 << j))
                displaycb[currentDisplayID - 1].setPoint(i + x,j + y,rgb);
            else
                displaycb[currentDisplayID - 1].setPoint(i + x,j + y,displayBackRGB);
        }
    }
}

/* 画ascii 5*7 字符
 * 函数名：void displayASCII_5x7(int16_t x,int16_t y,uint8_t n,char ch,uint32_t rgb)
 * 输入：
 *      x,y     坐标
 *      n       放大倍数
 *      ch      字符
 *      rgb     颜色
 * 返回：
 */
void displayASCII_5x7(int16_t x,int16_t y,uint8_t n,char ch,uint32_t rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;
    uint16_t i = 0,j = 0;
    uint8_t tmp = 0;
    for(i = 0;i < 5;i ++)
    {
        tmp = ascii5x7Lib[(ch - 0x20) * 5 + i];
        if(displayConversely)
            tmp = ~tmp;
        for(j = 0;j < 7;j ++)
        {
            if(tmp & (0x01 << j))
                displaycb[currentDisplayID - 1].setPoint(i + x,j + y,rgb);
            else
                displaycb[currentDisplayID - 1].setPoint(i + x,j + y,displayBackRGB);
        }
    }
}

/* 画ascii 8*8 字符
 * 函数名：void displayASCII_8x8(int16_t x,int16_t y,uint8_t n,char ch,uint32_t rgb)
 * 输入：
 *      x,y     坐标
 *      n       放大倍数
 *      ch      字符
 *      rgb     颜色
 * 返回：
 */
void displayASCII_8x8(int16_t x,int16_t y,uint8_t n,char ch,uint32_t rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;
    uint16_t i = 0,j = 0;
    uint8_t tmp = 0;
    for(i = 0;i < 8;i ++)
    {
        tmp = ascii8x8Lib[(ch - 0x20) * 8 + i];
        if(displayConversely)
            tmp = ~tmp;
        for(j = 0;j < 8;j ++)
        {
            if(tmp & (0x01 << j))
                displaycb[currentDisplayID - 1].setPoint(j + x,i + y,rgb);
            else
                displaycb[currentDisplayID - 1].setPoint(j + x,i + y,displayBackRGB);
        }
    }
}

/* 画ascii 8*16 字符
 * 函数名：void displayASCII_8x16(int16_t x,int16_t y,uint8_t n,char ch,uint32_t rgb)
 * 输入：
 *      x,y     坐标
 *      n       放大倍数
 *      ch      字符
 *      rgb     颜色
 * 返回：
 */
void displayASCII_8x16(int16_t x,int16_t y,uint8_t n,char ch,uint32_t rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;
    uint16_t i = 0,j = 0;
    uint8_t tmp = 0;
    for(i = 0;i < 16;i ++)
    {
        tmp = ascii8x16Lib[((ch - 0x20) * 16) + i];
        if(displayConversely)
            tmp = ~tmp;
        for(j = 0;j < 8;j ++)
        {
            if(tmp & (0x80 >> j))
                displaycb[currentDisplayID - 1].setPoint(j + x,i + y,rgb);
            else
                displaycb[currentDisplayID - 1].setPoint(j + x,i + y,displayBackRGB);
        }
    }
}

/* 画进度条
 * 函数名：void displayProgressBar(int16_t x,int16_t y,uint16_t l,uint16_t w,uint16_t s,uint16_t e,uint16_t current,uint8_t style,uint32_t rgb)
 * 输入：
 *      x,y     坐标
 *      l,w     长宽
 *      s,e     起始范围,终止范围
 *      current 当前值
 *      style   风格 默认1
 *      rgb     颜色
 *
 *      1.
 *       ___________________________
 *      |                           |
 *      |||||||||||||||||||||       |  70%
 *      |___________________________|
 *
 *      2.
 *       ___________________________
 *      |                   --------|
 *      |          ---------        |
 *      |----------|||||||||        |
 *      ||||||||||||||||||||        |
 *      |___________________________|
 *
 * 返回：
 */
void displayProgressBar(int16_t x,int16_t y,uint16_t l,uint16_t w,uint16_t s,uint16_t e,uint16_t current,uint8_t style,uint32_t rgb)
{
    if((!currentDisplayID) || (!displaycb[currentDisplayID - 1].setPoint))
        return;
    if((e == s) || (l < 4))
        return;
    uint16_t pos = x + 2; 
	if(current > e) current = e;
    float per = ((float)(current - s) / (e - s));
    switch(style)
    {
        case 1:
            pos = (uint16_t)(per * (l - 4)); 
            displayRectangle(x,y,l,w,1,rgb);
            display("\033[%d;%dH%d%%\033[0m",x + l + 1,y + (w / 2) - 3,(uint8_t)(per * 100) % 101);
            displayRectangle(x + 2,y + 2,l - 4,w - 4,((l > w) ? l : w),0);
            l = pos; w = w - 4;
            if(l > 1) 
                displayRectangle(x + 2,y + 2,l,w,((l > w) ? l : w),rgb);
            else if(l == 1)
                displayLine(x + 2,y + 2,x + 2,y + 2 + w - 1,1,rgb);
        break;

        case 2:
            pos = (uint16_t)(per * (l - 4)); 
            displayRectangle(x,y,l,w,((l > w) ? l : w),0);
            displayRectangle(x,y,l,w,1,rgb);

            displayTriangle(x + 2,y + w - 3,x + l - 3,y + w - 3,x + l - 3,y + 2,1,rgb);

            for(uint16_t i = 0;i < pos;i ++)
            {
                per = (float)i / (l - 4);
                displayLine(x + 2 + i,y + 2 + (uint8_t)((w - 4) * (1 - per)) + ((per == 0) ? (-1) : 0),x + 2 + i,y + w - 3,1,rgb);
            }
        break;

        default:

        break;
    }
}

/* 滚动屏幕
 * 函数名：void displayFrameMove(uint16_t l,uint16_t h,int32_t *line,int32_t *column,int16_t d,uint8_t style)
 * 输入：
 *      l,h     屏幕长,高
 *      line    行偏移指针
 *      column  列偏移指针
 *      d       移动距离,正数向下,右;负数向上,左
 *      style   移动方式,0,复位,1 左右移动;2 上下移动
 * 返回：
 */
void displayFrameMove(uint16_t l,uint16_t h,int32_t *line,int32_t *column,int16_t d,uint8_t style)
{
    if(style == 2)
    {
        *line += d; 
        if(((*line) > h) || ((*line) < -h))
            *line = 0;
    }
    else if(style == 1)
    {
        *column += d; 
        if((*column > l) || (*column < -l))
            *column = 0;
    }
    else if(style == 0)
    {
        *line = 0;
        *column = 0;
    }
}
 
#endif

/*********************一下函数来自网络,略做修改**********************/
static void msc_printchar(char **str, int c)
{
	if(str) 
    {
		**str = c;
		++(*str);
	}
	else 
    {
        if(!currentDisplayID)
            return;
        if(displaycb[currentDisplayID - 1].putChar)
            displaycb[currentDisplayID - 1].putChar(c);
    }
}

#define PAD_RIGHT   1
#define PAD_ZERO    2

static int msc_prints(char **out, const char *string, int width, int pad)
{
	register int pc = 0, padchar = ' ';

	if(width > 0) 
    {
		register int len = 0;
		register const char *ptr;
		for(ptr = string; *ptr; ++ ptr) 
        {
            ++ len;
        }
		if(len >= width) 
        {
            width = 0;
        }
		else
        {
            width -= len;
        }
		if(pad & PAD_ZERO) 
        {
            padchar = '0';
        }
	}
	if(!(pad & PAD_RIGHT)) 
    {
		for( ; width > 0; -- width) 
        {
			msc_printchar(out, padchar);
			++ pc;
		}
	}
	for( ; *string ; ++string) 
    {
		msc_printchar(out, *string);
		++ pc;
	}
	for( ; width > 0; --width)
    {
		msc_printchar(out, padchar);
		++ pc;
	}

	return pc;
}

/* the following should be enough for 32 bit int */
#define PRINT_BUF_LEN (48)

static int msc_printi(char **out, int i, int b, int sg, int width, int pad, int letbase)
{
	char print_buf[PRINT_BUF_LEN] = {0};
	register char *s;
	register int t, neg = 0, pc = 0;
	register unsigned int u = i;

	if(i == 0) 
    {
		print_buf[0] = '0';
		print_buf[1] = '\0';
		return msc_prints(out, print_buf, width, pad);
	}
	if(sg && b == 10 && i < 0) 
    {
		neg = 1;
		u = -i;
	}
	s = print_buf + PRINT_BUF_LEN - 1;
	*s = '\0';

	while(u)
    {
		t = u % b;
		if( t >= 10 )
			t += letbase - '0' - 10;
		*--s = t + '0';
		u /= b;
	}

	if(neg)
    {
		if(width && (pad & PAD_ZERO))
        {
			msc_printchar(out, '-');
			++ pc;
			-- width;
		}
		else
        {
			*--s = '-';
		}
	}

	return pc + msc_prints(out, s, width, pad);
}

static int msc_print_f(char **out, double f,int widthf,int widthb, int pad)
{
	char print_buf[PRINT_BUF_LEN] = {0};
	register char *s;
	register int t, neg = 0, pc = 0;
    register unsigned int u,d,dl = widthb;
    uint32_t baseb = pow(10,widthb);
    if(f < 0) 
    {
		neg = 1;
        f = -f;
	}
	
    f = f + (0.5 / baseb);
    u = (unsigned int)f;
	d = (unsigned int)((f - u) * baseb) % baseb;
	
	s = print_buf + PRINT_BUF_LEN-1;
	*s = '\0';

    do
    {
		t = d % 10;
		*--s = t + '0';
		d /= 10;
        dl --;
	}while(d || dl);
    *--s = '.';
	do
    {
		t = u % 10;
		*--s = t + '0';
		u /= 10;
	}while(u);

	if(neg)
    {
		if(widthf && (pad & PAD_ZERO))
        {
			msc_printchar(out, '-');
			++ pc;
			-- widthf;
		}
		else
        {
			*--s = '-';
		}
	}

	return pc + msc_prints(out, s, widthf + widthb + 1, pad);
}


#define CLEAR_SCREEN_CMD    "^\033[2J"
#define SET_XY_CMD          "^\033[%d;%dH"
#define SET_XYZ_CMD         "^\033[%d;%d;%dH"
#define SET_FRGB_CMD        "^\033[%dm"
#define SET_FBRGB_CMD       "^\033[%d;%dm"
#define SET_CONV_CMD        "^\033[7m"
#define SET_RECV_CMD        "^\033[0m"

static int msc_print(char **out, const char *format, va_list args)
{
	register int width, widthb = 3, pad;
	register int pc = 0;
	char scr[2];

	for(; *format != 0; ++ format) 
    {
        while(*format == 0x1b)
        {
            if(mscStrStr((char *)format,CLEAR_SCREEN_CMD)) 
            {
                if(currentDisplayID && displaycb[currentDisplayID - 1].clear)
                    displaycb[currentDisplayID - 1].clear();
                format += 4;
            }
            else if(mscStrStr((char *)format,SET_CONV_CMD)) 
            {
                if(currentDisplayID && displaycb[currentDisplayID - 1].setConversely)
                    displaycb[currentDisplayID - 1].setConversely(1);
                format += 4;
            }
            else if(mscStrStr((char *)format,SET_RECV_CMD)) 
            {
                if(currentDisplayID && displaycb[currentDisplayID - 1].setConversely)
                    displaycb[currentDisplayID - 1].setConversely(0);
                format += 4;
            }
            else if(mscStrStr((char *)format,SET_XY_CMD)) 
            {
                int x = va_arg(args,int);
                int y = va_arg(args,int);
                if(currentDisplayID && displaycb[currentDisplayID - 1].setXYZ)
                    displaycb[currentDisplayID - 1].setXYZ(x,y,0xffffffff);
                format += 8;
            }
            else if(mscStrStr((char *)format,SET_XYZ_CMD)) 
            {
                int x = va_arg(args,int);
                int y = va_arg(args,int);
                int z = va_arg(args,int);
                if(currentDisplayID && displaycb[currentDisplayID - 1].setXYZ)
                    displaycb[currentDisplayID - 1].setXYZ(x,y,z);
                format += 11;
            }
            else if(mscStrStr((char *)format,SET_FRGB_CMD)) 
            {
                uint32_t frgb = va_arg(args,uint32_t);
                if(currentDisplayID && displaycb[currentDisplayID - 1].setFrontRGB)
                    displaycb[currentDisplayID - 1].setFrontRGB(frgb);
                format += 5;
            }
            else if(mscStrStr((char *)format,SET_FBRGB_CMD)) 
            {
                uint32_t frgb = va_arg(args,uint32_t);
                uint32_t brgb = va_arg(args,uint32_t);
                if(currentDisplayID && displaycb[currentDisplayID - 1].setFrontRGB)
                    displaycb[currentDisplayID - 1].setFrontRGB(frgb);
                if(currentDisplayID && displaycb[currentDisplayID - 1].setBackRGB)
                    displaycb[currentDisplayID - 1].setBackRGB(brgb);
                format += 8;
            }
            else
            {
                break;
            }
        }
        if(*format == '\0') 
        {
            break;
        }
        else if(*format == '\n')
        {
            if(currentDisplayID && displaycb[currentDisplayID - 1].setXYZ)
                    displaycb[currentDisplayID - 1].setXYZ(0xffffffff,0xfffffffe,0xffffffff);
			++ format;
        }
        else if(*format == '\r')
        {
            if(currentDisplayID && displaycb[currentDisplayID - 1].setXYZ)
                    displaycb[currentDisplayID - 1].setXYZ(0,0xffffffff,0xffffffff);
			++ format;
        }
        else if(*format == '%') 
        {
			++ format;
			width = pad = 0;
            widthb = 3;
			if (*format == '\0') break;
			if (*format == '%') goto out;
			if (*format == '-') 
            {
				++ format;
				pad = PAD_RIGHT;
			}
			while(*format == '0') 
            {
				++ format;
				pad |= PAD_ZERO;
			}
			for( ; *format >= '0' && *format <= '9'; ++ format)
            {
				width *= 10;
				width += *format - '0';
			}
            if(*format == '.')
            {
                for(widthb = 0, ++ format; *format >= '0' && *format <= '9'; ++ format)
                {
                    widthb *= 10;
                    widthb += *format - '0';
                }
                if(!widthb) widthb = 3;
            }
			if(*format == 's') 
            {
				register char *s = (char *)va_arg(args,char *);
				pc += msc_prints(out,s ? s : "(null)", width, pad);
				continue;
			}
            else if(*format == 'd')
            {
				pc += msc_printi(out, va_arg(args, int), 10, 1, width, pad, 'a');
				continue;
			}
            else if(*format == 'x') 
            {
				pc += msc_printi(out, va_arg(args, int), 16, 0, width, pad, 'a');
				continue;
			}
            else if(*format == 'X')
            {
				pc += msc_printi(out, va_arg(args, int), 16, 0, width, pad, 'A');
				continue;
			}
            else if(*format == 'u') 
            {
				pc += msc_printi(out, va_arg(args, int), 10, 0, width, pad, 'a');
				continue;
			}
            else if(*format == 'f') 
            {
				pc += msc_print_f(out, va_arg(args,double), width, widthb, pad);
				continue;
			}
            else if(*format == 'c') 
            {
				/* char are converted to int then pushed on the stack */
				scr[0] = (char)va_arg(args, int);
				scr[1] = '\0';
				pc += msc_prints(out, scr, width, pad);
				continue;
			}
		}
		else
        {
		out:
			msc_printchar(out, *format);
			++ pc;
		}
	}
	if(out) 
    {
        **out = '\0';
    }
	va_end(args);
	return pc;
}

int mscPrintf(const char *format, ...)
{
        va_list args;
        va_start(args,format);
        return msc_print(0, format, args);
}

int mscSprintf(char *out, const char *format, ...)
{
        va_list args;
        va_start(args, format);
        return msc_print(&out, format, args);
}

#endif

